package top.ply.common_unit.network;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.ply.config.ConstantPool;

import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

// 发起网络请求的工具类，使用HttpClient实现
public class NetWork {

    private static Logger logger = LoggerFactory.getLogger(NetWork.class);

    private static HttpClient httpClient;

    private static RequestConfig requestConfig;

    static {
        SocketConfig socketConfig = SocketConfig.custom()
                .setSoTimeout(ConstantPool.NET_SOCKET_TIMEOUT)
                .setSoKeepAlive(true)
                .setTcpNoDelay(true)
                .build();

        requestConfig = RequestConfig.custom()
                .setSocketTimeout(ConstantPool.NET_SOCKET_TIMEOUT)
                .setConnectTimeout(ConstantPool.NET_CONNECTION_TIMEOUT)
                .build();

        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
        manager.setDefaultSocketConfig(socketConfig);
        manager.setMaxTotal(ConstantPool.NET_MAX_CONNECTION);
        manager.setDefaultMaxPerRoute(ConstantPool.NET_MAX_CONCURRENT_CONNECTION);

        httpClient = HttpClients.custom()
                .setConnectionManager(manager)
                .setRetryHandler(new CustomRetryHandler()).build();
    }

    public static Map<String, String> doGet(String url, Map<String, String> datas, Map<String, String> headers) {
        try {
            // 构建uri
            URIBuilder uriBuilder = new URIBuilder(url);
            // 将请求参数添加到uri中
            if (datas != null) {
                for (Map.Entry<String, String> data : datas.entrySet()) {
                    uriBuilder.addParameter(data.getKey(), data.getValue());
                }
            }
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            // 添加请求头
            if (headers != null) {
                for (Map.Entry<String, String> header : headers.entrySet()) {
                    httpGet.addHeader(header.getKey(), header.getValue());
                }
            }

            // 添加配置项
            httpGet.setConfig(requestConfig);
            // 发起请求
            HttpResponse response = httpClient.execute(httpGet);
            // 获取请求数据并转为map对象返回
            ObjectMapper om = new ObjectMapper();
            Map<String, String> data = om.readValue(
                    EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8),
                    new TypeReference<Map<String, String>>(){});
            return data;

        } catch (URISyntaxException e) {
            logger.error("Get服务被传递了一个错误的URL", e);
        } catch (ClientProtocolException e) {
            logger.error("Get服务协议异常", e);
        } catch (JsonMappingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (JsonProcessingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (IOException e) {
            logger.error("Get服务IO异常", e);
        }
        return new HashMap<>();
    }

    public static Map<String, String> doPost(String url, Map<String, String> datas, Map<String, String> headers) {
        try {
            // 构建uri
            URIBuilder uriBuilder = new URIBuilder(url);
            // 将请求参数添加到uri中
            if (datas != null) {
                for (Map.Entry<String, String> data : datas.entrySet()) {
                    uriBuilder.addParameter(data.getKey(), data.getValue());
                }
            }
            HttpPost httpPost = new HttpPost(uriBuilder.build());
            // 添加请求头
            if (headers != null) {
                for (Map.Entry<String, String> header : headers.entrySet()) {
                    httpPost.addHeader(header.getKey(), header.getValue());
                }
            }

            // 添加配置项
            httpPost.setConfig(requestConfig);
            // 发起请求
            HttpResponse response = httpClient.execute(httpPost);
            // 获取请求数据并转为map对象返回
            ObjectMapper om = new ObjectMapper();
            Map<String, String> data = om.readValue(
                    EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8),
                    new TypeReference<Map<String, String>>(){});
            return data;

        } catch (URISyntaxException e) {
            logger.error("Get服务被传递了一个错误的URL", e);
        } catch (ClientProtocolException e) {
            logger.error("Get服务协议异常", e);
        } catch (JsonMappingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (JsonProcessingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (IOException e) {
            logger.error("Get服务IO异常", e);
        }
        return new HashMap<>();
    }

    private static class CustomRetryHandler implements HttpRequestRetryHandler {

        @Override
        public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
            if (executionCount >= ConstantPool.NET_MAX_FAIL_TIME) { // 超过重试次数
                return false;
            }
            if (exception instanceof InterruptedIOException) { // IO被打断
                return true;
            }
            if (exception instanceof UnknownHostException) { // 未知主机
                return false;
            }
            if (exception instanceof ConnectTimeoutException) { // 超时
                return true;
            }
            if (exception instanceof SSLException) { // SSL握手异常
                return false;
            }
            // 判断是否为幂等请求
            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
            return idempotent;

        }
    }
}
